#pragma warning(disable : 4996)
#include "Image.h"
#include <iostream>
#include <cmath>

void Image::operator -()
{
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            for (int rgb = 0; rgb < 3; rgb++) {
                data[i][j][rgb] = 255 - data[i][j][rgb];
            }

        }
    }
}

void Image::operator +(int m)
{
for (int i = 0; i < height; i++)
        for (int j = 0; j < width; j++)
             for (int k =0; k  <3;k++)
      {
                 data[i][j][k] += m;
                if (data[i][j][k] > 255)
                    data[i][j][k] = 255;
                else if (data[i][j][k] < 0)
                    data[i][j][k] = 0;
             }
}

void Image::to_barrel()
{
  double Q = 0.1;
    int h = height * 1.5;
    int w = width * 1.5;
    if (h%4!=0)
        h = (h*24/8+3)/4*4/3;
    if (w%4!=0){
        padding = w * 3 % 4 == 0 ? 0 : 4 - w * 3 % 4;
        if(padding>=2){
            w+=(4-padding)%4;
        }
        else if(padding>0){
            w-=padding;
        }
    }
    double *** temp;
    Alloc(temp,h,w);
    int row,col;
    int state_row,state_col;
    for (int i = 0; i < height; i++)
        for (int j = 0; j < width;j++)
        {

                    if (i < height / 4)
                    {
                        state_row = edge;
                        row = i;
                    }
                    else if (i < (0.5 - Q / 2) * height)
                    {
                        state_row = middle;
                        row = height / 4 + (i - height / 4) * 1.5;
                    }
                    else if (i < (0.5 + Q / 2) * height)
                    {
                        state_row = core;
                        row = height / 4 + height * (0.25 - Q / 2) * 1.5 + (i - height * (0.5 - Q / 2)) * 4;
                    }
                    else if (i < 0.75 * height)
                    {
                        state_row = middle;
                        row = height / 4 + height * (0.25 - Q / 2) * 1.5 + height * Q * 4 + (i - height * (0.5 + Q / 2)) * 1.5;
                    }
                    else
                    {
                        state_row = edge;
                        row = height / 4 + height * (0.25 - Q / 2) * 3 + height * Q * 4 + (i - 0.75 * height);
                    }

                    if (j < width / 4)
                             {
                                 state_col = edge;
                                 col = j;
                             }
                             else if (j < (0.5 - Q / 2) * width)
                             {
                                 state_col = middle;
                                 col = width / 4 + (j - width / 4) * 1.5;
                             }
                             else if (j < (0.5 + Q / 2) * width)
                             {
                                 state_col = core;
                                 col = width / 4 + width * (0.25 - Q / 2) * 1.5 + (j - width * (0.5 - Q / 2)) * 4;
                             }
                             else if (j < 0.75 * width)
                             {
                                 state_col = middle;
                                 col = width / 4 + width * (0.25 - Q / 2) * 1.5 + width * Q * 4 + (j - width * (0.5 + Q / 2)) * 1.5;
                             }
                             else
                             {
                                 state_col = edge;
                                 col = width / 4 + width * (0.25 - Q / 2) * 3 + width * Q * 4 + (j - 0.75 * width);
                             }

          if (state_row == edge && state_col == edge)
                     {
                             for (int k = 0; k< 3;k++)
              temp[row][col][k] = data[i][j][k];
             }

          if (state_row == edge && state_col == middle)
             {
                       for (int k = 0; k< 3;k++)
              {
                           temp[row][col][k] = data[i][j][k];
                             temp[row][col+1][k] = data[i][j][k];
          }
                       }

           if (state_row == edge && state_col == core)
           {
               for (int k = 0; k< 3;k++)
             {
                   temp[row][col-1][k] = data[i][j][k];
                   temp[row][col][k] = data[i][j][k];
                   temp[row][col+1][k] = data[i][j][k];
                     temp[row][col+2][k] = data[i][j][k];
                         temp[row][col+3][k] = data[i][j][k];
               }
           }

           if (state_row == middle && state_col == edge)
           {
                for (int k = 0; k< 3;k++)
                {
                     temp[row][col][k] = data[i][j][k];
                    temp[row+1][col][k] = data[i][j][k];
                }
           }

           if (state_row == middle && state_col == middle)
           {
                for (int k = 0; k< 3;k++)
                {
                     temp[row][col][k] = data[i][j][k];
                    temp[row][col+1][k] = data[i][j][k];
                    temp[row][col+2][k] = data[i][j][k];
                       for (int G = 0;G < 3;G++)
                      {
                           temp[row+1][col+G][k] = temp[row][col + G][k];
}
                }
           }

           if (state_row == middle && state_col == core)
           {
                for (int k = 0; k< 3;k++)
                {
                       temp[row][col-1][k] = data[i][j][k];
                     temp[row][col][k] = data[i][j][k];
                      temp[row][col+1][k] = data[i][j][k];
                      temp[row][col+2][k] = data[i][j][k];
                      temp[row][col+3][k] = data[i][j][k];

                     for (int G = -1;G < 4;G++)
                    {
                         temp[row+1][col+G][k] = temp[row][col + G][k];
}
                }
                }

           if (state_row == core && state_col == edge)
           {
               for (int k = 0; k < 3;k++)
               {
                    temp[row][col][k] = data[i][j][k];
                    temp[row+1][col][k] = data[i][j][k];
                     temp[row+2][col][k] = data[i][j][k];
                      temp[row+3][col][k] = data[i][j][k];
                      temp[row+4][col][k] = temp[row][col][k];
                      temp[row+5][col][k] = temp[row][col][k];
               }
           }

               if (state_row == core && state_col == middle)
               {
                   for (int k = 0; k < 3;k++)
                   {
                         temp[row][col][k] = data[i][j][k];

                              temp[row][col+1][k] = data[i][j][k];
                            for (int G = 0; G < 2; G++)
                            {
                                temp[row+1][col+G][k] = temp[row][col+G][k];
                               temp[row+2][col+G][k] = temp[row][col+G][k];
                               temp[row+3][col+G][k] = temp[row][col+G][k];
                               temp[row+4][col+G][k] = temp[row][col + G][k];
                               temp[row+5][col+G][k] = temp[row][col+G][k];
                            }

                   }
               }


          if (state_row == core && state_col == core)
         {
              for (int k = 0; k < 3;k++)
              {
            temp[row][col-1][k] = temp[row][col][k] = temp[row][col+1][k] = temp[row][col+2][k] = temp[row][col+3][k] =   data[i][j][k];
                for (int G = -1 ; G < 4; G++)
                {
                    temp[row+1][col+G][k] = temp[row][col + G][k];
                     temp[row+2][col+G][k] = temp[row][col + G][k];
                      temp[row+3][col+G][k] = temp[row][col + G][k];
                      temp[row+4][col+G][k] = temp[row][col + G][k];
                      temp[row+5][col+G][k] = temp[row][col+G][k];
                }
              }
          }
        }

    Free(data,height,width);
      data = temp;
      height = h;
      width = w;
      this->MedianFilter(5);
}
  void Image::warm_cold_change(double m)
 {
     if (m >= 0)
     {
       for (int i = 0; i <height; i++)
           for (int j = 0; j <width;j++){
               data[i][j][0] += m;
               data[i][j][1] += m;
               data[i][j][2] -= m;
           }
    }
     else if(m < 0)
     {
         for (int i = 0; i <height; i++)
             for (int j = 0; j <width;j++){
                 data[i][j][0] += m/3;
                 data[i][j][2] -= m/3;
             }

     }
 }

  void Image:: gray2bw(double t)
 {
     double stand;
     for (int i = 0; i < height; i++)
            for (int j = 0; j < width; j++)
              {
                stand = (data[i][j][0] + data[i][j][1] + data[i][j][2])/3.0;
                if (stand >= t)
                    data[i][j][0] = data[i][j][1] = data[i][j][2] = 255;
                else data[i][j][0] = data[i][j][1] = data[i][j][2]  = 0;
            }
}


Image::Image() :Matrix()
{

}

Image::Image(int h, int w)
{
    height = h;
    width = w;
    if (width % 4 != 0)
        width = (width * 24 / 8 + 3) / 4 * 4 / 3;
    Alloc(data, height, width);
}

Image::Image(const char* ImageName)
{
    Frequent(ImageName, data, height, width);
}

void Image::Frequent(const char* filename, double***& data, int& height, int& width)
{
    int i, j;
    FILE* fp = nullptr;
    fopen_s(&fp, filename, "rb");
    fseek(fp, 18, 0);
    fread(&width, 4, 1, fp);
    fread(&height, 4, 1, fp);
    if (width % 4 != 0)
        width = (width * 24 / 8 + 3) / 4 * 4 / 3;
    Alloc(data, height, width);
    unsigned char* temp = new unsigned char[width * 3];
    fseek(fp, 54, 0);
    for (i = 0; i < height; i++)
    {
        fread(temp, width * 3, 1, fp);
        for (j = 0; j < width; j++) {
            data[i][j][0] = temp[3 * j];
            data[i][j][1] = temp[3 * j + 1];
            data[i][j][2] = temp[3 * j + 2];
        }
    }
    delete[]temp;
    fclose(fp);
}


Image::Image(double* m, int h, int w)
{
    height = h;
    width = w;
    Alloc(data, height, width);
    for (int i = 0; i < height; i++)
        for (int j = 0; j < width; j++) {
            for (int rgb = 0; rgb < 3; rgb++) {
                data[i][j][rgb] = m[(i + 1) * (j + 1) * 3 - 3 + rgb];
            }
        }
}

Image::Image(const Matrix& m)
{
    height = m.Height();
    width = m.Width();
    Alloc(data, height, width);
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            for (int rgb = 0; rgb < 3; rgb++) {
                data[i][j][rgb] = m.At(i, j, rgb);
            }
        }
    }
}

Image::Image(const Image& im)
{
    height = im.height;
    width = im.width;
    Alloc(data, height, width);
    for (int i = 0; i < height; i++)
        for (int j = 0; j < width; j++) {
            for (int rgb = 0; rgb < 3; rgb++) {
                data[i][j][rgb] = im.data[i][j][rgb];
            }
        }
}


Image::~Image()
{

}



void Image::ReadBMP(const char* ImageName)
{
    if (data == nullptr)
    {
        Frequent(ImageName, data, height, width);
    }
    else {
        Free(data, height, width);
        Frequent(ImageName, data, height, width);
    }
}

void Image::WriteBMP(const char* filename)
{
    FILE* fp = nullptr;
    fopen_s(&fp, filename, "wb");
    short s = 0x4d42;
    unsigned long L;
    fwrite(&s, 2, 1, fp);
    L = 0;
    fwrite(&L, 4, 1, fp);
    s = 0;
    fwrite(&s, 2, 1, fp);
    fwrite(&s, 2, 1, fp);
    L = 54;
    fwrite(&L, 4, 1, fp);
    L = 40;
    fwrite(&L, 4, 1, fp);
    fwrite(&width, 4, 1, fp);
    fwrite(&height, 4, 1, fp);
    s = 1;
    fwrite(&s, 2, 1, fp);
    s = 24;
    fwrite(&s, 2, 1, fp);
    L = 0;
    for (int i = 0; i < 6; i++)
        fwrite(&L, 4, 1, fp);
    unsigned char temp;
    for (int i = 0; i < height; i++)
        for (int j = 0; j < width; j++)
        {
            for (int rgb = 0; rgb < 3; rgb++) {
                temp = data[i][j][rgb];
                fwrite(&temp, 1, 1, fp);
            }
        }
    fclose(fp);
}

void Image::Flip(int code)
{
    if (code == 0)
    {
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < (width + 1) / 2; j++)
            {
                for (int rgb = 0; rgb < 3; rgb++) {
                    double temp = data[i][width - j - 1][rgb];
                    data[i][width - j - 1][rgb] = data[i][j][rgb];
                    data[i][j][rgb] = temp;
                }
            }
        }
    }
    else if (code == 1)
    {
        for (int j = 0; j < width; j++) {

            for (int i = 0; i < (height + 1) / 2; i++)
            {
                for (int rgb = 0; rgb < 3; rgb++) {
                    double temp = data[height - i - 1][j][rgb];
                    data[height - i - 1][j][rgb] = data[i][j][rgb];
                    data[i][j][rgb] = temp;
                }
            }
        }
    }
}


void Image::Resize(int h, int w)
{
    padding = w * 3 % 4 == 0 ? 0 : 4 - w * 3 % 4;
    if(padding>=2){
        w+=(4-padding)%4;
    }
    else if(padding>0){
        w-=padding;
    }
    double*** beta;
    double hi = height * 1.0 / h, wi = width * 1.0 / w, hn = 0, wn = 0;
    int ii = 0, jj = 0,i=0,j=0;

    Alloc(beta, h, w);
    for (i = 0; i < h; i++) {
        for (j = 0; j < w; j++) {
            for(int rgb=0;rgb<3;rgb++){
                beta[i][j][rgb] = data[ii][jj][rgb];
            }
            wn += wi;
            while (wn >= 1) {
                jj++; wn--;
            }
        }
        hn += hi;
        while (hn >= 1) {
            ii++; hn--;
        }
        jj = 0;
        wn = 0;
    }

    Free(data, height,width);
    width = w;
    height = h;

    Alloc(data, height, width);
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            for(int rgb=0;rgb<3;rgb++){
                data[i][j][rgb] = beta[i][j][rgb];
            }
        }
    }
    Free(beta, height,width);
}

void Image::Cut(int x1, int y1, int x2, int y2)
{
    int i,j;
    double*** temp = nullptr;
    height = y2 - y1;
    width = x2 - x1;

    padding = width * 3 % 4 == 0 ? 0 : 4 - width * 3 % 4;
    if(padding>=2){
        width+=(4-padding)%4;
    }
    else if(padding>0){
        width-=padding;
    }


    Alloc(temp, height, width);
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            for (int rgb = 0; rgb < 3; rgb++) {
                temp[i][j][rgb] = data[x1+i][y1+j][rgb];
            }
        }
    }
    Free(data, height, width);

    Alloc(data, height, width);
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            for (int rgb = 0; rgb < 3; rgb++) {
                data[i][j][rgb] = temp[i][j][rgb];
            }
        }
    }
}

void Image::Rotate_Left()
{
       padding = height * 3 % 4 == 0 ? 0 : 4 - height * 3 % 4;
       height-=padding;
       int old_height = height;
       int old_width = width;
       width = height;
       height = old_width;
       double*** temp = nullptr;
       Alloc(temp, height, width);
       for (int i = 0; i < old_height; i++){
           for (int j = 0; j < old_width; j++){
               for(int rgb=0;rgb<3;rgb++){
                   temp[j][old_height - i - 1][rgb] = data[i][j][rgb];
               }
           }
       }
       Free(data, old_height,old_width);
       Alloc(data, height, width);
       for (int i = 0; i < height; i++){
           for (int j = 0; j < width; j++){
               for(int rgb=0;rgb<3;rgb++){
                   data[i][j][rgb] = temp[i][j][rgb];
               }
           }
       }
         Free(temp, height,width);
}

void Image::Rotate_Right()
{
       padding = height * 3 % 4 == 0 ? 0 : 4 - height * 3 % 4;
       height-=padding;
       int old_height = height;
       int old_width = width;
       width = height;
       height = old_width;
       double*** temp = nullptr;
       Alloc(temp, height, width);
                   for (int i = 0; i < old_height; i++){
                       for (int j = 0; j < old_width; j++){
                           for(int rgb=0;rgb<3;rgb++){
                               temp[old_width - 1 - j][i][rgb] = data[i][j][rgb];
                           }
                       }
                   }
                   Free(data, old_height,old_width);
                   Alloc(data, height, width);
                   for (int i = 0; i < height; i++){
                       for (int j = 0; j < width; j++){
                           for(int rgb=0;rgb<3;rgb++){
                               data[i][j][rgb] = temp[i][j][rgb];
                           }
                       }
                   }
                     Free(temp, height,width);

}

void Image::Turned_Over()
{
   for (int j = 0; j < width / 2; j++)
   {
       for (int i = 0; i < height; i++)
       {
           for(int rgb=0;rgb<3;rgb++){
               double temp = data[i][j][rgb];
               data[i][j][rgb] = data[height - 1 - i][width - 1 - j][rgb];
               data[height - 1 - i][width - 1 - j][rgb] = temp;
           }
       }
   }
}

void Image::gray(){
    double gg;
    for(int i=0;i<height;i++){
        for(int j=0;j<width;j++){
            gg=data[i][j][0] * 0.299 + data[i][j][1] * 0.587 + data[i][j][2] * 0.114;
            data[i][j][0]=data[i][j][1]=data[i][j][2]=gg;
        }
    }
}

void Image::choose(){

}

Sobel Image::turn_to_sobel() {
    Sobel output(height,width);
    for(int i=0;i<height;i++){
        for(int j=0;j<width;j++){
            output.data[i][j]=(data[i][j][0]*0.114+data[i][j][1]*0.587+data[i][j][2]*0.299)/3;
        }
    }
    return output;
}

Image::Image(const Sobel &sobel){
    height=sobel.height;
    width=sobel.width;
    Alloc(data,height,width);
    for(int i=0;i<height;i++){
        for(int j=0;j<width;j++){
            for(int rgb=0;rgb<3;rgb++){
                data[i][j][rgb]=sobel.data[i][j];
            }
        }
    }
}

Image sobel(Image input){
    Sobel loading(1,1);//随便创建一个sobel对象 反正有运算符重载
    loading=input.turn_to_sobel();
    Sobel gaussian(1,1);
    gaussian=GaussianBlur(loading);
    Sobel imageSobelX(input.Height(),input.Width()),imageSobelY(input.Height(),input.Width());
    double *point;
    SobelGradDirction(gaussian,imageSobelX,imageSobelY,point);
    Sobel pre_turning(input.height,input.width);
    SobelAmplitude(imageSobelX,imageSobelY,pre_turning);
    Image output(pre_turning);
    return output;
}
